package com.yangrd.airship.generator.support.impl;

import com.yangrd.airship.generator.support.PageGeneratorDefineMetaHook;
import com.yangrd.airship.generator.support.PageGeneratorWriter;
import com.yangrd.airship.generator.support.PageTemplate;
import com.yangrd.airship.generator.support.PageTemplateResolver;
import com.yangrd.airship.generator.support.utils.FileScanUtils;
import com.yangrd.airship.generator.support.utils.ObjMapper;
import com.yangrd.airship.generator.support.utils.StringUtils;
import com.yangrd.airship.generator.page.PageGeneratorDefine;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author yangrd
 * @date 2021/11/10
 */
@Slf4j
@RequiredArgsConstructor
public class FreemarkerPageTemplateResolver implements PageTemplateResolver {

    static Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);

    static String TEMPLATE_PATH = "table-generator-templates";

    protected final PageGeneratorDefineMetaHook metaHook;

    static {
        // Specify the source where the template files come from. Here I set a
        // plain directory for it, but non-file-system sources are possible too:
        cfg.setClassLoaderForTemplateLoading(FreemarkerPageTemplateResolver.class.getClassLoader(), TEMPLATE_PATH);
        // Set the preferred charset template files are stored in. UTF-8 is
        // a good choice in most applications:
        cfg.setDefaultEncoding("UTF-8");
        // Sets how errors will appear.
        // During web page *development* TemplateExceptionHandler.HTML_DEBUG_HANDLER is better.
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
    }


    @Override
    public PageTemplate resolver(Predicate<File> fileFilter) {
        try {
            List<File> files = FileScanUtils.scanListFile(ResourceUtils.getFile("classpath:" + TEMPLATE_PATH), (file, fileName) -> (fileName.contains(".ftl") || file.isDirectory()))
                    .stream().filter(fileFilter).collect(Collectors.toList());
            return new FreemarkerPageTemplate(ObjMapper.mapList(files, file -> {
                String path = getPath(file);
                String name = file.getName();
                try {
                    Template template = cfg.getTemplate(path + "/" + name);
                    return TemplateInfo.of(path, name, template);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


    @RequiredArgsConstructor
    class FreemarkerPageTemplate implements PageTemplate {

        private final List<TemplateInfo> templateInfos;

        @Override
        public void gen(Object data, PageGeneratorWriter writer) throws Exception {

            for (TemplateInfo templateInfo : templateInfos) {

                String fullName = templateInfo.getName();
                if (data instanceof PageGeneratorDefine) {
                    metaHook.handler((PageGeneratorDefine) data, templateInfo);
                    fullName = ((PageGeneratorDefine) data).getName() + getSuffix(templateInfo.getName());
                }

                List<Writer> writers = writer.listWriter(PageGeneratorWriter.TableGeneratorWriterContext.of(templateInfo.getPath(), fullName));

                log.info("template {}/{}", templateInfo.getPath(), templateInfo.getName());
                for (Writer w : writers) {
                    templateInfo.getTemplate().process(data, w);
                }
            }
        }


        String getSuffix(String name) {
            name = name.replace(".ftl", "");
            name = name.replace("model", "");
            return (name.indexOf("\\.") == 0 ? name : StringUtils.capFirst(name));
        }
    }


    @Value(staticConstructor = "of")
    public static class TemplateInfo {

        String path;

        String name;

        Template template;
    }

    private static String getPath(File file) {
        return file.getPath().split(TEMPLATE_PATH)[1].replace("/" + file.getName(), "");
    }


}
